﻿using System.Net;
using System.Text;
using Blazor.CoffeeTalesApi.Dto;
using Blazor.CoffeeTalesApi.Dto.Request;
using Blazor.CoffeeTalesApi.Entity;
using Blazor.Model.Dto;
using Blazor.Model.Entity;
using Blazor.Util;
using Microsoft.Extensions.Logging;
using Newtonsoft.Json;

namespace Blazor.CoffeeTalesApi;

public class ApiClient(ILogger logger, Uri mainlandApiUri, Uri overseasApiUri) : IDisposable
{
    private readonly SafeHttpClient _client = new(logger);

    private StringContent GetJsonContent(string content) => new(content, Encoding.UTF8, "application/json");

    private Uri GetApiBaseUri(Channel channel = default)
    {
        return channel switch
        {
            Channel.ForceLogin => mainlandApiUri,
            Channel.QooApp => overseasApiUri,
            Channel.GooglePlay => overseasApiUri,
            Channel.AppStore => overseasApiUri,
            Channel.TapTapMainland => mainlandApiUri,
            Channel.BiliBiliMainland => mainlandApiUri,
            Channel.AppStoreMainland => mainlandApiUri,
            _ => throw new ArgumentOutOfRangeException(nameof(channel), channel, null)
        };
    }

    public async Task<IPAddress?> FetchMyIpAsync()
    {
        var response = await _client.GetOrDefaultAsync(new Uri(GetApiBaseUri(), "/ip"));
        if (!response.IsSuccessStatusCode)
        {
            return null;
        }

        var content = await response.Content.ReadAsStringAsync();
        return IPAddress.TryParse(content, out var address) ? address : null;
    }

    public async Task<ChannelInfo?> FetchChannelInfoAsync(Channel channel)
    {
        var response = await _client.GetOrDefaultAsync(new Uri(GetApiBaseUri(channel), $"/channel_setting/info/{channel}"));
        if (response is not { IsSuccessStatusCode: true })
        {
            return null;
        }

        var content = await response.Content.ReadAsStringAsync();
        return JsonConvert.DeserializeObject<ChannelInfo>(content);
    }

    public async Task<UpdateInfo?> FetchUpdateInfoAsync(Channel channel, uint version, string deviceId)
    {
        var response = await _client.GetOrDefaultAsync(new Uri(GetApiBaseUri(channel), $"/update_info?channel={channel}&version={version}&device_id={deviceId}"));
        if (response is not { IsSuccessStatusCode: true })
        {
            return null;
        }

        var content = await response.Content.ReadAsStringAsync();
        return JsonConvert.DeserializeObject<UpdateInfo>(content);
    }

    public async Task<bool> SetNativeVersionAsync(Channel channel, uint latestVersion, uint minVersion)
    {
        var request = new SetNativeVersionRequest()
        {
            Channel = channel,
            LatestNativeVersion = latestVersion,
            MinNativeVersion = minVersion
        };

        var s = JsonConvert.SerializeObject(request);
        var uri = new Uri(GetApiBaseUri(channel), "/channel_setting/native_version");
        var response = await _client.PutOrDefaultAsync(uri, GetJsonContent(s));
        return response is { IsSuccessStatusCode: true };
    }

    public async Task<bool> SetJumpUrlAsync(Channel channel, Uri uri)
    {
        var s = JsonConvert.SerializeObject(new SetJumpUrlRequest()
        {
            Channel = channel,
            JumpUrl = uri.AbsoluteUri
        });
        var serverUri = new Uri(GetApiBaseUri(channel), "/channel_setting/jump_url");
        var response = await _client.PutOrDefaultAsync(serverUri, GetJsonContent(s));
        return response is { IsSuccessStatusCode: true };
    }

    public async Task<bool> AddHotUpdateSettingAsync(Channel channel, uint nativeVersion, ResourceVersionEntry resourceVersion,
        string resourceUrl)
    {
        var request = new AddHotUpdateSettingRequest
        {
            Channel = channel,
            NativeVersion = nativeVersion,
            ResourceVersion = resourceVersion,
            ResourceUrl = resourceUrl,
        };

        var s = JsonConvert.SerializeObject(request);
        var serverUri = new Uri(GetApiBaseUri(channel), "/hot_update_setting");
        var response = await _client.PostOrDefaultAsync(serverUri, GetJsonContent(s));
        return response is { IsSuccessStatusCode: true };
    }

    public async Task<bool> DeleteHotUpdateSettingAsync(Channel channel, uint nativeVersion)
    {
        var serverUri = new Uri(GetApiBaseUri(channel), $"/hot_update_setting/{channel}/{nativeVersion}");
        var response = await _client.DeleteOrDefaultAsync(serverUri);
        return response is { IsSuccessStatusCode: true };
    }

    public async Task<bool> SetResourceVersionAsync(Channel channel, uint nativeVersion,
        ResourceVersionEntry resourceVersion)
    {
        var request = new SetResourceVersionRequest
        {
            Channel = channel,
            NativeVersion = nativeVersion,
            ResourceVersion = resourceVersion
        };

        var s = JsonConvert.SerializeObject(request);

        var serverUri = new Uri(GetApiBaseUri(channel), "/hot_update_setting/resource_version");
        var response = await _client.PutOrDefaultAsync(serverUri, GetJsonContent(s));
        return response is { IsSuccessStatusCode: true };
    }

    public async Task<bool> SetResourceUrlAsync(Channel channel, uint nativeVersion, string url)
    {
        var s = JsonConvert.SerializeObject(new SetResourceUrlRequest()
        {
            Channel = channel,
            NativeVersion = nativeVersion,
            Url = url,
        });
        var serverUri = new Uri(GetApiBaseUri(channel), "/hot_update_setting/resource_url");
        var response = await _client.PutOrDefaultAsync(serverUri, GetJsonContent(s));
        return response is { IsSuccessStatusCode: true };
    }

    public async Task<bool> AddWhitelistAsync(Channel channel, uint nativeVersion, IWhitelistKey key,
        ResourceVersionEntry resourceVersion)
    {
        var s = JsonConvert.SerializeObject(new AddOrUpdateWhitelistRequest()
        {
            Channel = channel,
            NativeVersion = nativeVersion,
            Key = key.GetJsonRepresent(),
            ResourceVersion = resourceVersion,
        });
        var serverUri = new Uri(GetApiBaseUri(channel), "/whitelist");
        var response = await _client.PostOrDefaultAsync(serverUri, GetJsonContent(s));
        return response is { IsSuccessStatusCode: true };
    }

    public async Task<bool> UpdateWhitelistAsync(Channel channel, uint nativeVersion, IWhitelistKey key,
        ResourceVersionEntry resourceVersion)
    {
        var s = JsonConvert.SerializeObject(new AddOrUpdateWhitelistRequest()
        {
            Channel = channel,
            NativeVersion = nativeVersion,
            Key = key.GetJsonRepresent(),
            ResourceVersion = resourceVersion,
        });

        var serverUri = new Uri(GetApiBaseUri(channel), "/whitelist");
        var response = await _client.PutOrDefaultAsync(serverUri, GetJsonContent(s));
        return response is { IsSuccessStatusCode: true };
    }

    public async Task<bool> RemoveWhitelistAsync(Channel channel, uint nativeVersion, IWhitelistKey key)
    {
        string url;
        if (key is IpKey ipKey)
        {
            url = $"/whitelist/by/ip/{channel}/{nativeVersion}/{ipKey}";
        }else if (key is DeviceIdKey deviceIdKey)
        {
            url = $"/whitelist/by/device_id/{channel}/{nativeVersion}/{deviceIdKey}";
        }
        else
        {
            return false;
        }


        if (string.IsNullOrWhiteSpace(url))
        {
            return false;
        }

        var uri = new Uri(GetApiBaseUri(channel), url);
        var response = await _client.DeleteOrDefaultAsync(uri);
        return response is { IsSuccessStatusCode: true };
    }

    public async Task<bool> ClearWhitelistAsync(Channel channel, uint nativeVersion)
    {
        var uri = new Uri(GetApiBaseUri(channel), $"/whitelist/clear/{channel}/{nativeVersion}");
        var response = await _client.DeleteOrDefaultAsync(uri);
        return response is { IsSuccessStatusCode: true };
    }

    public async Task<IDictionary<IWhitelistKey, ResourceVersionEntry>?> FetchWhitelistAsync(Channel channel,
        uint nativeVersion)
    {
        var uri = new Uri(GetApiBaseUri(channel), $"/whitelist/{channel}/{nativeVersion}");
        var response = await _client.GetOrDefaultAsync(uri);
        if (response is not { IsSuccessStatusCode: true })
        {
            return null;
        }

        var content = await response.Content.ReadAsStringAsync();
        return JsonConvert.DeserializeObject<Dictionary<IWhitelistKey, ResourceVersionEntry>>(content);
    }

    public async Task<IEnumerable<DeviceEntry>?> FetchAllDevicesAsync()
    {
        var uri = new Uri(mainlandApiUri, $"device/all");
        var response = await _client.GetOrDefaultAsync(uri);
        if (response is not { IsSuccessStatusCode: true })
        {
            return null;
        }

        var content = await response.Content.ReadAsStringAsync();
        return JsonConvert.DeserializeObject<List<DeviceEntry>>(content);
    }

    public async Task<bool> AddDevice(Guid id, string desc)
    {
        var json = JsonConvert.SerializeObject(new AddDeviceRequest
        {
            Id = id,
            Description = desc
        });

        var uri = new Uri(mainlandApiUri, $"/device");
        var response = await _client.PostOrDefaultAsync(uri, GetJsonContent(json));
        if (response is not { IsSuccessStatusCode: true })
        {
            return false;
        }

        return true;
    }

    public async Task<bool> RemoveDevice(Guid id)
    {
        var uri = new Uri(mainlandApiUri, $"/device/{id}");
        var response = await _client.DeleteOrDefaultAsync(uri);
        if (response is not { IsSuccessStatusCode: true })
        {
            return false;
        }

        return true;
    }

    public void Dispose()
    {
        _client.Dispose();
    }
}